import { h, Schema, HTTP } from "koishi"

export const name = "wiki-watcher"
export const inject = ["database"] // broadcast

export const Config = Schema.array(Schema.intersect([
  Schema.object({
    endpoint: Schema.string().required().role("url"),
    pollInterval: Schema.number().default(120000),
    namespaces: Schema.string().default("0|1|2|3|4|5|6|7|9|11|13|15|17"),
    channels: Schema.array(String),
  }),
  HTTP.Config,
]))

export function apply(ctx, configs) {
  for (const config of configs) plugin(ctx, config)

  ctx.i18n.define("zh-CN", "lnnbot.wiki-watcher", {
    anon: "匿名用户 {0}",
    comment: "摘要：{0}",
    event: {
      edit: "<panguspace after>{actor}</panguspace>编辑了<panguspace>{siteName}</panguspace>页面 {title}",
      new: "<panguspace after>{actor}</panguspace>创建了<panguspace>{siteName}</panguspace>页面 {title}",
      delete: "<panguspace after>{actor}</panguspace>删除了<panguspace>{siteName}</panguspace>页面 {title}",
      move: "<panguspace after>{actor}</panguspace>将<panguspace>{siteName}</panguspace>页面 {title} 移动至 {newTitle}",
      upload: "<panguspace after>{actor}</panguspace>在<panguspace>{siteName}</panguspace>上传了<panguspace before>{title}</panguspace>",
      block: "<panguspace after>{actor}</panguspace>封禁了<panguspace>{siteName}</panguspace>账号 {user}，期限为 {duration}",
      unblock: "<panguspace after>{actor}</panguspace>解封了<panguspace>{siteName}</panguspace>账号 {user}",
      "newusers.create": "<panguspace>{siteName}</panguspace>账号 {user} 被注册",
      "newusers.create2": "<panguspace after>{actor}</panguspace>注册了<panguspace>{siteName}</panguspace>账号 {user}",
      deleteuser: "<panguspace after>{actor}</panguspace>删除了<panguspace>{siteName}</panguspace>账号 {user}",
      import: "<panguspace after>{actor}</panguspace>在<panguspace>{siteName}</panguspace>导入了 {title} 的 {count} 个版本",
    },
    separator: "----",
    sizes: "{lenDiff} 字节，更改后 {newLen} 字节",
  })
}

function i18n(path, data) {
  if (data == null || Array.isArray(data)) return h.i18n(path, data)
  return h("i18n:object", { path, data })
}

function plugin(ctx, config) {
  let lastTimestamp
  let lastRcid = -Infinity

  const http = ctx.http.extend(config)
  let indexPhp = null
  let base = null
  let articlePath = null
  let siteName = null

  async function init() {
    try {
      const response = await http.get(config.endpoint, {
        params: {
          action: "query",
          format: "json",
          formatversion: "2",
          meta: "siteinfo",
          siprop: "general",
        },
        responseType: "json",
      })
      ctx.logger.debug("[init %s]", config.endpoint, response)
      base = response.query.general.base
      indexPhp = new URL(response.query.general.script, base).toString()
      articlePath = response.query.general.articlepath
      siteName = response.query.general.sitename
      ctx.logger.debug("[%s]", config.endpoint, { base, indexPhp, articlePath, siteName })
    } catch (err) {
      ctx.logger.error("[init %s]", config.endpoint, err)
      ctx.setTimeout(init, config.pollInterval)
      return
    }
    lastTimestamp = new Date().toISOString()

    ctx.setTimeout(pollRecentChanges, 0)//config.pollInterval)
  }

  function getDiffURL(revid) {
    const url = new URL(indexPhp)
    url.searchParams.set("diff", revid)
    return url.toString()
  }

  async function pollRecentChanges() {
    let response
    try {
      response = await http.get(config.endpoint, {
        params: {
          action: "query",
          format: "json",
          formatversion: "2",
          list: "recentchanges",
          rctype: "new|edit|log",
          rcprop: "title|ids|user|sizes|timestamp|parsedcomment|loginfo",
          rcnamespace: config.namespaces,
          rcstart: lastTimestamp,
          rcdir: "newer",
        },
        responseType: "json",
      })
      ctx.logger.debug("[%s]", config.endpoint, response)

      const recentChanges = response.query.recentchanges

      if (!recentChanges.length) return

      const lines = []
      recentChanges.forEach(change => {
        if (change.rcid <= lastRcid) return
        if (config.hideAutopatrolled && change.ns % 2 === 0 && change.autopatrolled) return

        const type = change.type
        const actor = change.anon
          ? i18n("lnnbot.wiki-watcher.anon", [change.user])
          : change.user
        const title = change.title
        const titleChopped = title.replace(/^.*?:/, "")

        if (type === "new" || type === "edit") {
          if (change.ns === 2 && title.replace(/^.*?:/, "").split("/", 1)[0] === change.user) return
          lines.push(
            i18n(`lnnbot.wiki-watcher.event.${type}`, { actor, title, siteName })
          )
          lines.push(getDiffURL(change.revid))
          const lendiff = change.newlen - change.oldlen
          lines.push(
            i18n("lnnbot.wiki-watcher.sizes", {
              lenDiff: lendiff < 0 ? lendiff : `+${lendiff}`,
              newLen: change.newlen,
              oldLen: change.oldlen,
            })
          )
        } else if (change.logtype === "delete" && change.logaction === "delete") {
          lines.push(
            i18n("lnnbot.wiki-watcher.event.delete", { actor, title, siteName })
          )
        } else if (change.logtype === "move") {
          lines.push(
            i18n("lnnbot.wiki-watcher.event.move", {
              actor,
              title,
              newTitle: change.logparams.target_title,
              siteName,
            })
          )
          lines.push(getDiffURL(change.revid))
        } else if (change.logtype === "upload") {
          lines.push(
            i18n("lnnbot.wiki-watcher.event.upload", { actor, title, siteName })
          )
          lines.push(getDiffURL(change.revid))
        } else if (change.logtype === "newusers" && ["byemail", "create"].includes(change.logaction)) {
            lines.push(
              i18n("lnnbot.wiki-watcher.event.newusers.create", { user: titleChopped, siteName })
            )
        } else if (change.logtype === "newusers" && change.logaction === "create2") {
          lines.push(
            i18n("lnnbot.wiki-watcher.event.newusers.create2", { actor, user: titleChopped, siteName })
          )
        } else if (change.logtype === "block" && change.logaction === "block") {
          lines.push(
            i18n("lnnbot.wiki-watcher.event.block", {
              actor,
              user: titleChopped,
              duration: change.logparams.duration,
              siteName,
            })
          )
        } else if (change.logtype === "block" && change.logaction === "unblock") {
          lines.push(
            i18n("lnnbot.wiki-watcher.event.unblock", { actor, user: titleChopped, siteName })
          )
        } else if (change.logtype === "usermerge" && change.logaction === "deleteuser") {
          lines.push(
            i18n("lnnbot.wiki-watcher.event.deleteuser", { actor, user: titleChopped, siteName })
          )
        } else if (change.logtype === "import") {
          lines.push(
            i18n("lnnbot.wiki-watcher.event.import", {
              actor,
              title,
              count: change.logparams.count,
              siteName,
            })
          )
          lines.push(getDiffURL(change.revid))
        } else return
        if (change.parsedcomment) {
          lines.push(
            i18n("lnnbot.wiki-watcher.comment", [
              h("", h.parse(change.parsedcomment)).toString(true),
            ])
          )
        }
        lines.push(i18n("lnnbot.wiki-watcher.separator"))
      })
      lines.pop()

      if (lines.length) {
        ctx.logger.debug("[%s]", config.endpoint, lines)
        await ctx.broadcast(
          config.channels.slice(0),
          lines.map(l => h("p", l))
        )
      }

      lastTimestamp = recentChanges.at(-1).timestamp
      lastRcid = recentChanges.at(-1).rcid
    } catch (err) {
      ctx.logger.error("[%s]", config.endpoint, err)
    } finally {
      ctx.setTimeout(pollRecentChanges, config.pollInterval)
    }
  }

  ctx.on("ready", init)
}
